Pressidian
花园入口
笔记
项目
关于
实验室
GitHub
花园入口
笔记
项目
关于
实验室
GitHub

KNOWLEDGE PATHS

笔记库
当前位置
笔记库/前端/三件套/JS/JavaScript

13 标准内建对象

16 分钟阅读 · Note

目录树 578 篇

            • 00 JavaScript学习地图
            • 01 JavaScript基础
            • 02 类型系统与类型转换
            • 03 Number与String
            • 04 Array数组
            • 05 Object对象
            • 06 集合与迭代
            • 07 函数、作用域与this
            • 08 原型、构造函数与类
            • 09 Generator与异步迭代
            • 10 异步编程
            • 11 浏览器与DOM
            • 12 二进制与文件
            • 13 标准内建对象
            • 14 模块化
            • 15 错误处理
            • 16 JSDoc
            • 90 JavaScript知识点总目录
            • 91 JavaScript修正与补充记录
          • 前端模块化
      • 前端技术栈
    • 笔记目录
    • CLAUDE.md
    • Vue 组件与 Render 函数

关联笔记 6

↗00 JavaScript学习地图反向链接↗90 JavaScript知识点总目录反向链接↗91 JavaScript修正与补充记录反向链接↗01 JavaScript基础同一路径↗02 类型系统与类型转换同一路径↗03 Number与String同一路径
  • 13 标准内建对象

13 标准内建对象

> Last Format Time:8/11/2026 21:45:40

本笔记由两个 JavaScript 教程目录中的同主题内容合并而成;全部旧知识保留在“旧笔记知识全集(按来源保留)”中,并由导航逐项索引。


当前前端开发关键要点

> [!important] 学习优先级:P1 > - 日期、正则和 JSON 都有明确边界:时区、正则灾难性回溯、不可序列化值需要特别留意。 > - 避免显式创建原始类型包装对象;使用对应静态方法或字面量。 > - Proxy 与 Reflect 适合元编程、响应式和校验封装,但会增加调试与性能成本。 > - 优先使用平台标准 API,并在兼容性要求明确后再决定是否引入 polyfill。


知识点导航

[现代JS教程与阮一峰ES6 · 5特殊内建对象/Date](./13 标准内建对象.md)

  • Date

[现代JS教程与阮一峰ES6 · 5特殊内建对象/JSON](./13 标准内建对象.md)

  • JSON

[现代JS教程与阮一峰ES6 · 5特殊内建对象/Proxy与Reflect](./13 标准内建对象.md)

  • Reflect

[廖雪峰教程 · 5标准对象/5.1 简述](./13 标准内建对象.md)

  • 包装对象

[廖雪峰教程 · 5标准对象/5.2 RegExp](./13 标准内建对象.md)

  • 进阶
  • RegExp
  • 切分字符串
  • 分组
  • 贪婪匹配
  • 全局搜索

现代前端补充与纠错

> [!info] 修改标记 > - 【修正】:旧教程中错误、过时或容易误导的内容。 > - 【补充】:旧教程未覆盖、但当前前端开发需要掌握的内容。 > - 【修正代码】:替换或校正了旧代码示例。

> [!warning] 下方保留教程原文;转换、国际化和日期处理的当前建议以本节为准。

数值与日期

  • 【修正】 Number(value) 与 parseInt/parseFloat 不等价:前者要求整体转换,后两者可读取合法前缀。
  • 【补充】 Date 同时混合时间点、时区展示与易错解析;传输使用明确格式,业务层明确时区,不要依赖含糊的本地日期字符串。
  • 【补充】 金额和高精度十进制不要直接依赖二进制浮点;BigInt 也不表示小数。

Intl

  • 【补充】 数字/货币:Intl.NumberFormat;日期时间:Intl.DateTimeFormat;排序比较:Intl.Collator。
  • 【补充】 相对时间、列表、复数规则、分词可分别使用 Intl.RelativeTimeFormat、ListFormat、PluralRules、Segmenter。
  • 【补充】 格式化器可复用,不要在大型循环里为每个值重复构造。

正则与序列化

  • 【补充】 动态拼入正则的用户文本必须转义;复杂文本解析不要强行用一个巨型正则完成。
  • 【补充】 JSON.stringify 不支持循环引用和 BigInt,并会忽略/改变部分值;序列化协议应有明确 schema 与版本。

旧笔记知识全集(按来源保留)

现代JS教程与阮一峰ES6 · 5特殊内建对象/Date

Date

调用 new Date() 来创建一个新的 Date 对象。在调用时可以带有一些参数,如下所示: 不带参数 —— 创建一个表示当前日期和时间的 Date 对象

new Date(milliseconds) 创建一个 Date 对象,其时间等于 1970 年 1 月 1 日 UTC+0 之后经过的毫秒数milliseconds(1/1000 秒)。

new Date(datestring) 如果只有一个参数,并且是字符串,那么它会被自动解析。该算法与 Date.parse 所使用的算法相同

new Date(year, month, date, hours, minutes, seconds, ms) 使用当前时区中的给定组件创建日期。只有前两个参数是必须的。

  • year 应该是四位数。为了兼容性,也接受 2 位数,并将其视为 19xx,例如 98 与 1998 相同,但强烈建议始终使用 4 位数。
  • month 计数从 0(一月)开始,到 11(十二月)结束。
  • date 是当月的具体某一天,如果缺失,则为默认值 1。
  • 如果 hours/minutes/seconds/ms 缺失,则均为默认值 0。

从 Date 对象中访问年、月等信息有多种方式: getFullYear() 获取年份(4 位数)很多 JavaScript 引擎都实现了一个非标准化的方法 getYear()。不推荐使用这个方法。它有时候可能会返回 2 位的年份信息。永远不要使用它。要获取年份就使用 getFullYear()。 getMonth() 获取月份,从 0 到 11。 getDate() 获取当月的具体日期,从 1 到 31,这个方法名称可能看起来有些令人疑惑。 getHours(),getMinutes(),getSeconds(),getMilliseconds() 获取相应的时间组件。

另外,我们还可以获取一周中的第几天: getDay() 获取一周中的第几天,从 0(星期日)到 6(星期六)。第一天始终是星期日,在某些国家可能不是这样的习惯,但是这不能被改变。

getTime() 返回日期的时间戳 —— 从 1970-1-1 00:00:00 UTC+0 开始到现在所经过的毫秒数。

getTimezoneOffset() 返回 UTC 与本地时区之间的时差,以分钟为单位

下列方法可以设置日期/时间组件:

  • setFullYear(year, [month], [date])
  • setMonth(month, [date])
  • setDate(date)
  • setHours(hour, [min], [sec], [ms])
  • setMinutes(min, [sec], [ms])
  • setSeconds(sec, [ms])
  • setMilliseconds(ms)
  • setTime(milliseconds)(使用自 1970-01-01 00:00:00 UTC+0 以来的毫秒数来设置整个日期) 以上方法除了 setTime() 都有 UTC 变体,例如:setUTCHours()。

自动校准 是 Date 对象的一个非常方便的特性。 假设我们要在日期 “28 Feb 2016” 上加 2 天。结果可能是 “2 Mar” 或 “1 Mar”,因为存在闰年。但是我们不需要考虑这些,只需要直接加 2 天,剩下的 Date 对象会帮我们处理

let date = new Date();
date.setDate(date.getDate() + 100);

console.log(date);
//2025-10-02T14:40:25.361Z

当 Date 对象被转化为数字时,得到的是对应的时间戳,与使用 date.getTime() 的结果相同 日期可以相减,相减的结果是以毫秒为单位时间差。

let start = new Date(); // 开始测量时间

// do the job
for (let i = 0; i < 100000000; i++) {
    let doSomething = i * i * i;
}

let end = new Date(); // 结束测量时间

console.log(`The loop took ${end - start} ms`);
//The loop took 41 ms

如果我们仅仅想要测量时间间隔,我们不需要 Date 对象

let start = Date.now(); // 开始测量时间

// do the job
for (let i = 0; i < 100000000; i++) {
    let doSomething = i * i * i;
}

let end = Date.now(); // 结束测量时间

console.log(`The loop took ${end - start} ms`);
//The loop took 43 ms

它相当于 new Date().getTime(),但它不会创建中间的 Date 对象。因此它更快,而且不会对垃圾回收造成额外的压力。 这种方法很多时候因为方便,又或是因性能方面的考虑而被采用,例如使用 JavaScript 编写游戏或其他的特殊应用场景。

Date.prese(): 字符串的格式应该为:YYYY-MM-DDTHH:mm:ss.sssZ,其中:

  • YYYY-MM-DD —— 日期:年-月-日。
  • 字符 "T" 是一个分隔符。
  • HH:mm:ss.sss —— 时间:小时,分钟,秒,毫秒。
  • 可选字符 'Z' 为 +-hh:mm 格式的时区。单个字符 Z 代表 UTC+0 时区。

简短形式也是可以的,比如 YYYY-MM-DD 或 YYYY-MM,甚至可以是 YYYY。

Date.parse(str) 调用会解析给定格式的字符串,并返回时间戳(自 1970-01-01 00:00:00 起所经过的毫秒数)。如果给定字符串的格式不正确,则返回 NaN。

现代JS教程与阮一峰ES6 · 5特殊内建对象/JSON

JSON

Javascript Object Notation

JavaScript 提供了如下方法:

  • JSON.stringify 将对象转换为 JSON。
  • JSON.parse 将 JSON 转换回对象。

JSON 编码的对象与对象字面量有几个重要的区别:

  • ==字符串使用双引号==。JSON 中没有单引号或反引号。所以 'John' 被转换为 "John"。
  • 对象==属性名称==也是==双引号==的。这是强制性的。所以 age:30 被转换成 "age":30。

JSON.stringify 也可以应用于原始(primitive)数据类型。 JSON 支持以下数据类型:

  • Objects { ... }
  • Arrays [ ... ]
  • Primitives:
    • strings,
    • numbers,
    • boolean values true/false,
    • null。

JSON 是语言无关的纯数据规范,因此一些特定于 JavaScript 的对象属性会被 JSON.stringify 跳过。即:

  • 函数属性(方法)。
  • Symbol 类型的键和值。
  • 存储 undefined 的属性。

重要的限制:==不得有循环引用==。 支持嵌套对象转换,并且可以自动对其进行转换。

let obj = {
    name: 'dano',
    age: 21,
    foods: {
        apple: 23,
        peal: 34,
        shit: {
            wee: 21,
            ui:8,
        }
    }
}

let json = JSON.stringify(obj);
console.log(json);
//{"name":"dano","age":21,"foods":{"apple":23,"peal":34,"shit":{"wee":21,"ui":8}}}

let json2 = JSON.stringify(obj, ( key,value) => {
    return ((key == 'name')) ? undefined : value;
})
console.log(json2);
//{"age":21,"foods":{"apple":23,"peal":34,"shit":{"wee":21,"ui":8}}}

JSON.stringify有三个参数:

  • value 要编码的对象。
  • replacer 要编码的属性数组或映射函数 function(key, value)。
  • space 用于格式化的空格数量。
{
    "age": 21,
    "foods": {
        "apple": 23,
        "peal": 34,
        "shit": {
            "wee": 21,
            "ui": 8
        }
    }
}

使用自定义的toJSON: 像 toString 进行字符串转换,对象也可以提供 toJSON 方法来进行 JSON 转换。如果可用,JSON.stringify 会自动调用它。

 let obj = {
    name: 'dano',
    age: 21,
    foods: {
        apple: 23,
        peal: 34,
        shit: {
            wee: 21,
            ui:8,
        }
    },

    toJSON() {
        return this.name;  //"dano"
    }
}

使用JSON.prase来将JSON转换为对象:

let object = JSON.parse(json);
console.log(object);
{
  name: 'dano',
  age: 21,
  foods: { apple: 23, peal: 34, shit: { wee: 21, ui: 8 } }
}

此函数有两个参数: str 要解析的 JSON 字符串。

reviver 可选的函数 function(key,value),该函数将为每个 (key, value) 对调用,并可以对值进行转换。

从服务器上获取的JSON的data的值是字符串,使用reviver将其转换为Data。其对嵌套的对象也起作用

let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';

let meetup = JSON.parse(str, function (key, value) {
    if (key == 'date') return new Date(value);
    return value;
});

console.log(meetup.date.getDate());//30

现代JS教程与阮一峰ES6 · 5特殊内建对象/Proxy与Reflect

proxy代理器,用于拦截某些操作,修改原生的操作:

let proxy = new Proxy(target, handler)
  • target —— 是要包装的对象,可以是任何东西,包括函数。
  • handler —— 代理配置:带有“捕捉器”(“traps”,即拦截操作的方法)的对象。比如 get 捕捉器用于读取 target 的属性,set 捕捉器用于写入 target 的属性,等等。

对 proxy 进行操作,如果在 handler 中存在相应的捕捉器,则handler将运行,否则将直接对 target 进行处理:

let target = {};
let proxy = new Proxy(target, {}); // 空的 handler 对象

proxy.test = 5; // 写入 proxy 对象 (1)
alert(target.test); // 5,test 属性出现在了 target 中!

alert(proxy.test); // 5,我们也可以从 proxy 对象读取它 (2)

for(let key in proxy) alert(key); // test,迭代也正常工作 (3)

![Pasted image 20250628171616](../../../../_assets/84b7e11-Pasted image 20250628171616.png) 带有get的代理器:

let dictionary = {
  'Hello': 'Hola',
  'Bye': 'Adiós'
};

dictionary = new Proxy(dictionary, {
  get(target, phrase) { // 拦截读取属性操作
    if (phrase in target) { //如果词典中有该短语
      return target[phrase]; // 返回其翻译
    } else {
      // 否则返回未翻译的短语
      return phrase;
    }
  }
});

// 在词典中查找任意短语!
// 最坏的情况也只是它们没有被翻译。
alert( dictionary['Hello'] ); // Hola
alert( dictionary['Welcome to Proxy']); // Welcome to Proxy(没有被翻译)

set:

 let numbers = [];

numbers = new Proxy(numbers, { // (*)
  set(target, prop, val) { // 拦截写入属性操作
    if (typeof val == 'number') {
      target[prop] = val;
      return true;
    } else {
      return false;
    }
  }
});

numbers.push(1); // 添加成功
numbers.push(2); // 添加成功
alert("Length is: " + numbers.length); // 2

numbers.push("test"); // TypeError(proxy 的 'set' 返回 false)

alert("This line is never reached (error in the line above)");

没看完

Reflect

Reflect 是一个内建对象,可简化 Proxy 的创建。 前面所讲过的内部方法,例如 Get 和 Set 等,都只是规范性的,不能直接调用。 Reflect 对象使调用这些内部方法成为了可能。它的方法是内部方法的最小包装。

廖雪峰教程 · 5标准对象/5.1 简述

在JavaScript的世界里,一切都是对象。

但是某些对象还是和其他对象不太一样。为了区分对象的类型,我们用typeof操作符获取对象的类型,它总是返回一个字符串:

typeof 123; // 'number'
typeof 123n; // 'bigint'
typeof NaN; // 'number'
typeof 'str'; // 'string'
typeof true; // 'boolean'
typeof undefined; // 'undefined'
typeof Math.abs; // 'function'
typeof null; // 'object'
typeof []; // 'object'
typeof {}; // 'object'

可见,number、bigint、string、boolean、function和undefined有别于其他类型。特别注意null的类型是object,Array的类型也是object,如果我们用typeof将无法区分出null、Array和通常意义上的object——{}。

包装对象

除了这些类型外,JavaScript还提供了包装对象,熟悉Java的小伙伴肯定很清楚int和Integer这种暧昧关系。

number、boolean和string都有包装对象。没错,在JavaScript中,字符串也区分string类型和它的包装类型。包装对象用new创建:

let n = new Number(123); // 123,生成了新的包装类型
let b = new Boolean(true); // true,生成了新的包装类型
let s = new String('str'); // 'str',生成了新的包装类型

虽然包装对象看上去和原来的值一模一样,显示出来也是一模一样,但他们的类型已经变为object了!所以,包装对象和原始值用===比较会返回false:

typeof new Number(123); // 'object'
new Number(123) === 123; // false

typeof new Boolean(true); // 'object'
new Boolean(true) === true; // false

typeof new String('str'); // 'object'
new String('str') === 'str'; // false

所以_闲的蛋疼也不要使用包装对象_!尤其是针对string类型!!!

如果我们在使用Number、Boolean和String时,没有写new会发生什么情况?

此时,Number()、Boolean和String()被当做普通函数,把任何类型的数据转换为number、boolean和string类型(注意不是其包装类型):

*8/11/26 【修正】原内容:`Number()` 相当于 `parseInt()` 或 `parseFloat()`;修改点:三者转换规则不同。*
let n = Number('123'); // 123;但 Number、parseInt、parseFloat 的解析语义不同
typeof n; // 'number'

let b = Boolean('true'); // true
typeof b; // 'boolean'

let b2 = Boolean('false'); // true! 'false'字符串转换结果为true!因为它是非空字符串!
let b3 = Boolean(''); // false

let s = String(123.45); // '123.45'
typeof s; // 'string'

是不是感觉头大了?这就是JavaScript特有的催眠魅力!

总结一下,有这么几条规则需要遵守:

  • 不要使用new Number()、new Boolean()、new String()创建包装对象;
  • 用parseInt()或parseFloat()来转换任意类型到number;
  • 用String()来转换任意类型到string,或者直接调用某个对象的toString()方法;
  • 通常不必把任意类型转换为boolean再判断,因为可以直接写if (myVar) {...};
  • typeof操作符可以判断出number、boolean、string、function和undefined;
  • 判断Array要使用Array.isArray(arr);
  • 判断null请使用myVar === null;
  • 判断某个全局变量是否存在用typeof window.myVar === 'undefined';
  • 函数内部判断某个变量是否存在用typeof myVar === 'undefined'。

最后有细心的同学指出,任何对象都有toString()方法吗?null和undefined就没有!确实如此,这两个特殊值要除外,虽然null还伪装成了object类型。

更细心的同学指出,number对象调用toString()报SyntaxError:

123.toString(); // SyntaxError

遇到这种情况,要特殊处理一下:

123..toString(); // '123', 注意是两个点!
(123).toString(); // '123'

不要问为什么,这就是JavaScript代码的乐趣!

廖雪峰教程 · 5标准对象/5.2 RegExp

正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。

所以我们判断一个字符串是否是合法的Email的方法是:

  1. 创建一个匹配Email的正则表达式;
  2. 用该正则表达式去匹配用户的输入来判断是否合法。

因为正则表达式也是用字符串表示的,所以,我们要首先了解如何用字符来描述字符。

在正则表达式中,如果直接给出字符,就是精确匹配。用\d可以匹配一个数字,\w可以匹配一个字母或数字,所以:

  • '00\d'可以匹配'007',但无法匹配'00A';
  • '\d\d\d'可以匹配'010';
  • '\w\w'可以匹配'js';

.可以匹配任意字符,所以:

  • 'js.'可以匹配'jsp'、'jss'、'js!'等等。

要匹配变长的字符,在正则表达式中,用*表示任意个字符(包括0个),用+表示至少一个字符,用?表示0个或1个字符,用{n}表示n个字符,用{n,m}表示n-m个字符:

来看一个复杂的例子:\d{3}\s+\d{3,8}。

我们来从左到右解读一下:

  1. \d{3}表示匹配3个数字,例如'010';
  2. \s可以匹配一个空格(也包括Tab等空白符),所以\s+表示至少有一个空格,例如匹配' ','\t\t'等;
  3. \d{3,8}表示3-8个数字,例如'1234567'。

综合起来,上面的正则表达式可以匹配以任意个空格隔开的带区号的电话号码。

如果要匹配'010-12345'这样的号码呢?由于'-'是特殊字符,在正则表达式中,要用'\'转义,所以,上面的正则是\d{3}\-\d{3,8}。

但是,仍然无法匹配'010 - 12345',因为带有空格。所以我们需要更复杂的匹配方式。

进阶

要做更精确地匹配,可以用[]表示范围,比如:

  • [0-9a-zA-Z\_]可以匹配一个数字、字母或者下划线;
  • [0-9a-zA-Z\_]+可以匹配至少由一个数字、字母或者下划线组成的字符串,比如'a100','0_Z','js2015'等等;
  • [a-zA-Z\_\$][0-9a-zA-Z\_\$]*可以匹配由字母或下划线、开头,后接任意个由一个数字、字母或者下划线、开头,后接任意个由一个数字、字母或者下划线、组成的字符串,也就是JavaScript允许的变量名;
  • [a-zA-Z\_\$][0-9a-zA-Z\_\$]{0, 19}更精确地限制了变量的长度是1-20个字符(前面1个字符+后面最多19个字符)。

A|B可以匹配A或B,所以(J|j)ava(S|s)cript可以匹配'JavaScript'、'Javascript'、'javaScript'或者'javascript'。

^表示行的开头,^\d表示必须以数字开头。

$表示行的结束,\d$表示必须以数字结束。

你可能注意到了,js也可以匹配'jsp',但是加上^js$就变成了整行匹配,就只能匹配'js'了。

RegExp

有了准备知识,我们就可以在JavaScript中使用正则表达式了。

JavaScript有两种方式创建一个正则表达式:

第一种方式是直接通过/正则表达式/写出来,第二种方式是通过new RegExp('正则表达式')创建一个RegExp对象。

两种写法是一样的:

let re1 = /ABC\-001/;
let re2 = new RegExp('ABC\\-001');

re1; // /ABC\-001/
re2; // /ABC\-001/

注意,如果使用第二种写法,因为字符串的转义问题,字符串的两个\\实际上是一个\。

先看看如何判断正则表达式是否匹配:

let re = /^\d{3}\-\d{3,8}$/;
re.test('010-12345'); // true
re.test('010-1234x'); // false
re.test('010 12345'); // false

RegExp对象的test()方法用于测试给定的字符串是否符合条件。

切分字符串

用正则表达式切分字符串比用固定的字符更灵活,请看正常的切分代码:

'a b   c'.split(' '); // ['a', 'b', '', '', 'c']

嗯,无法识别连续的空格,用正则表达式试试:

'a b   c'.split(/\s+/); // ['a', 'b', 'c']

无论多少个空格都可以正常分割。加入,试试:

'a,b, c  d'.split(/[\s\,]+/); // ['a', 'b', 'c', 'd']

再加入;试试:

'a,b;; c  d'.split(/[\s\,\;]+/); // ['a', 'b', 'c', 'd']

如果用户输入了一组标签,下次记得用正则表达式来把不规范的输入转化成正确的数组。

分组

除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()表示的就是要提取的分组(Group)。比如:

^(\d{3})-(\d{3,8})$分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:

let re = /^(\d{3})-(\d{3,8})$/;
re.exec('010-12345'); // ['010-12345', '010', '12345']
re.exec('010 12345'); // null

如果正则表达式中定义了组,就可以在RegExp对象上用exec()方法提取出子串来。

exec()方法在匹配成功后,会返回一个Array,第一个元素是正则表达式匹配到的整个字符串,后面的字符串表示匹配成功的子串。

exec()方法在匹配失败时返回null。

提取子串非常有用。来看一个更凶残的例子:

let re = /^(0[0-9]|1[0-9]|2[0-3]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])$/;
re.exec('19:05:30'); // ['19:05:30', '19', '05', '30']

这个正则表达式可以直接识别合法的时间。但是有些时候,用正则表达式也无法做到完全验证,比如识别日期:

let re = /^(0[1-9]|1[0-2]|[0-9])-(0[1-9]|1[0-9]|2[0-9]|3[0-1]|[0-9])$/;

对于'2-30','4-31'这样的非法日期,用正则还是识别不了,或者说写出来非常困难,这时就需要程序配合识别了。

贪婪匹配

需要特别指出的是,正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。举例如下,匹配出数字后面的0:

let re = /^(\d+)(0*)$/;
re.exec('102300'); // ['102300', '102300', '']

由于\d+采用贪婪匹配,直接把后面的0全部匹配了,结果0*只能匹配空字符串了。

必须让\d+采用非贪婪匹配(也就是尽可能少匹配),才能把后面的0匹配出来,加个?就可以让\d+采用非贪婪匹配:

let re = /^(\d+?)(0*)$/;
re.exec('102300'); // ['102300', '1023', '00']
全局搜索

JavaScript的正则表达式还有几个特殊的标志,最常用的是g,表示全局匹配:

let r1 = /test/g;
// 等价于:
let r2 = new RegExp('test', 'g');

全局匹配可以多次执行exec()方法来搜索一个匹配的字符串。当我们指定g标志后,每次运行exec(),正则表达式本身会更新lastIndex属性,表示上次匹配到的最后索引:

let s = 'JavaScript, VBScript, JScript and ECMAScript';
let re=/[a-zA-Z]+Script/g;

// 使用全局匹配:
re.exec(s); // ['JavaScript']
re.lastIndex; // 10

re.exec(s); // ['VBScript']
re.lastIndex; // 20

re.exec(s); // ['JScript']
re.lastIndex; // 29

re.exec(s); // ['ECMAScript']
re.lastIndex; // 44

re.exec(s); // null,直到结束仍没有匹配到

全局匹配类似搜索,因此不能使用/^...$/,那样只会最多匹配一次。

正则表达式还可以指定i标志,表示忽略大小写,m标志,表示执行多行匹配。